#!/usr/bin/env python

"""
An echo client that allows the user to send multiple lines to the server.
Entering a blank line will exit the client.
"""

import getopt
import socket
import sys
import select
import time
import os
import libnet
from libnet.constants import * 
import pycap.capture
from threading import Thread
from threading import Event

size = 1024
ADDR=(0,0)
_thread_invoked=None
_run_main=None
_interface=None


class recv_class(Thread):

    def __init__(self,cli_sock,serv_ip,serv_port,cli_ip,cli_port,p):
        Thread.__init__(self)
        self.cli_sock = cli_sock
        self.serv_ip = serv_ip
        self.serv_port = serv_port
        self.cli_ip = cli_ip
        self.cli_port = cli_port
        self.p = p
        self.i=0
        
    def run(self):
        global _thread_invoked
        global _run_main
        packet=None
        myflag = 1

        while(_thread_invoked==1):
            try:
                if(packet != None):
                    protocol = int(packet[1].protocol)
                    dest_ip = packet[1].destination
                    source_ip = packet[1].source
                    dest_port = packet[2].destinationport
                    source_port = packet[2].sourceport
                else:
                    myflag=0
            except AttributeError:
                print "Attr Error"
                myflag=0

            if(myflag == 1):
                if((packet != None) and (protocol == 17)):
                    if(source_port == int(self.serv_port)) and (dest_port == int(self.cli_port)):
                    
                        if(source_ip == self.serv_ip and dest_ip == self.cli_ip):
                            sys.stdout.write('%')   
                            sys.stdout.flush()

                            print packet[3]
                            if(packet[3]=='\exit'):
                                print "Received Exit Command"
                                print packet[3]
                                _run_main=0
                                sys.exit()
                            sys.stdout.write('%')   
                            sys.stdout.flush()
                packet=self.p.next()
            else:
                time.sleep(1)
                packet=self.p.next()
                myflag=1
        sys.exit()        

def send_packet(cli_sock,data,dest_addr_tup,cli_port):
    global _interface
    dest_ip = dest_addr_tup[0]
    dest_port = dest_addr_tup[1] 
    
    #l=libnet.context(RAW4, device='lo')
    l=libnet.context(RAW4, device = _interface)
    
    dst_ip=l.name2addr4(dest_ip,DONT_RESOLVE)
    udp_tag=l.build_udp(dp=int(dest_port),sp=int(cli_port),payload=data)

    ipv4_tag=l.autobuild_ipv4(len=IPV4_H+UDP_H, prot=IPPROTO_UDP, dst=dst_ip)
    l.write()
    
def establish_connection(optlist):
    global _interface
    
    myserver=optlist[1]
    myserverport=int(optlist[2])
    myip=optlist[3]
    myport=int(optlist[4])

    myname=optlist[6]
    _interface = optlist[5]
    
    ADDR=(myserver,myserverport)
    print "ADDR: ",ADDR
    
    host = myserver
    port = myserverport
    
    print "[CONSOLE] ***Client now Started***\n"
    print "[CONSOLE] Connecting to server @%s, PORT: %d" %(host,port)
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    if(_interface == 'lo'):
        s.bind(('',myport))
    else:
        s.bind((myip,myport))
        
    print "[CONSOLE] Client Socket Created\n"
    
    """Register with server"""
    print "[CONSOLE] Registering with the server (Blocking statement. Hit ctrl+C to exit)"
    send_packet(s,'#'+myname,ADDR,myport)
    data,junk_tup = s.recvfrom(size)
    print "[CONSOLE] ",data.upper()
    print "Connected using ",junk_tup
    
    if (data.upper()=='DONE'):
        print "[CONSOLE] Registered with the server... \n"
        print "==============================================================="
        print "[CONSOLE] ***Connected as \'%s\' via port %d***" %(myname,myport)
        print "\nWELCOME TO CHATROOM"
        print "GROUP CHAT: "
        print "\tType message and hit enter to send to all in the chat room"
        print "PRIVATE CHAT"
        print "\tEXAMPLE: \\@clientname:Hello, This is a private chat"
        print "\tClient name must exist"
        print "EXIT:" 
        print "\tType \\exit on the terminal and hit enter"
        print "==============================================================="

        print "\n Entering chatroom:\n"
    else:
        print "[CONSOLE]Registration failed. Check server IP address and port no."
        sys.exit()

    return s


def check_special_command(sp_str,mysock,MYADDR,myport,th):    
    global _run_main
    global _thread_invoked

    if('\exit' in sp_str):
        send_packet(mysock,'\exit',MYADDR,myport)
        print "[CONSOLE] Exit Command detected. Closing connection"
        _run_main = 0 
        _thread_invoked = 0
        
def check_valid_ip(input_ip):
    """Function to check if the IP entered by user is sane"""
    frag_add_int = [0,0,0,0]
    frag_add=''
    frag_add = input_ip.split('.')
    i=0
    
    for i,temp in enumerate(frag_add):
        try:
            frag_add_int[i] = int(frag_add[i])
        except:
            print "Error. Only numbers allowed in IP address"
            return False
        
    if((len(frag_add_int) != 4) or\
       (frag_add_int[0] >255) or\
       (frag_add_int[1] >255) or\
       (frag_add_int[2] >255) or\
       (frag_add_int[3] >255)):
        return False
    else:
        return True

def check_valid_port(input_port):
    """Function to check if the port entered by user is sane"""

    try:
        int_port = int(input_port)
    except:
        print "Error in the input port %s" %(input_port)
        print "Only numbers between 1-65535 allowed"
        return False
    
    if((int_port<0) or (int_port>65535)):
        return False
        print "Error in the input port %s" %(input_port)   
        print "Only numbers between 1-65535 allowed"

    else:
        return True
    
    
def usage():
    print "USAGE:"
    print "IMPORTANT"
    print "!!!  Run application as sudo !!!"
    print "!!!  Avoid extra spaces. No spaces requires within the options See Example below !!!"

    print "Ex:"
    
    print "***Using loopback***"
    print "bash3.2# python client.py --serverport=4500 --serverip=127.0.0.1 --clientport=9877 --clientip=127.0.0.1 --interface=lo --clientname=maulik"
    print "OR"
    print "***Using network***"
    print "bash 3.2# python client.py --serverport=4500 --serverip=10.0.0.9 --clientport=9877 --clientip=10.0.0.3 --interface=eth1 --clientname=maulik"
    
    print "\nALL options Required for execution\n"
    print "--serverport="
    print "\t<Port on which server is listening for connections>"
    print "\t Integer value required"

    print "--serverip="
    print "\t<Ip address of server on which clients would connect>"
    print "\t Valid IP address required."

    print "--clientport="
    print "\t<Port on which client is listening for server data>"
    print "\t Integer value required"

    print "--clientip="
    print "\t<Ip address of client. Server must be able to ping on this address>"
    print "\t Valid IP address required."
    
    print "--interface="
    print "\t<Interface for listening. Set to \'lo\' if using loopback>"
    print "\t lo,eth0,eth1,bro etc. Do an \'ifconfig\' on linux terminal for list of available interfaces."

    print "--clientname="
    print "\t<Name of the client on the chat room>"
    print "\t No spaces allowed.."

    sys.exit()

def clrscr():
    """This function clears the screen according to the OS it is on."""
    if os.name == "posix":
            os.system('clear')
    else:
            os.system('CLS')

def mygetopt(myargv):
    
    myserver = ''
    myname = ''
    myport = ''
    myinterface = ''
    myip = ''
    
    if(len(myargv) > 4):
            opt,args = getopt.getopt(myargv[1:],"h:",['serverip=','serverport=','clientip=','clientport=','clientname=','interface='])	
            for option,value in opt:
                    if option == "-h" or option == "--help":
                            usage()
                    elif option == "--serverip":
                            myserver = value
                            if (check_valid_ip(myserver)):
                                pass
                            else:
                                myserver = ''
                                print "[ERROR] INVALID server IP Address"
                                usage() 
                    elif option in "--serverport":
                            myserverport=value
                            if (check_valid_port(myserverport)):
                                pass
                            else:
                                myserverport = ''
                                print "[ERROR] INVALID server port Address"
                                usage() 
                    elif option in "--clientname":
                            myname=value
                    elif option in "--clientport":
                            myport=value
                            if (check_valid_port(myport)):
                                pass
                            else:
                                myport = ''
                                print "[ERROR] INVALID client port Address"
                                usage() 
                    elif option in "--interface":
                            myinterface=value
                    elif option in "--clientip":
                            myip=value
                            if (check_valid_ip(myip)):
                                pass
                            else:
                                myip = ''
                                print "[ERROR] INVALID client IP Address"
                                usage() 
                    else:
                            print "Unhandled option"
                            usage()
    else:
            usage()

    if(myserver[0:2] == '127') or(myinterface == 'lo'):
        print "[WARNING] Overriding Interface %s" %(myinterface)
        myinterface='lo'
        print "Using IP address: %s on interface %s" %(myserver,myinterface)
    return [1,myserver,myserverport,myip,myport,myinterface,myname]

def main():
    clrscr()
    global _thread_invoked
    global _run_main
    optlist=[]
    optlist=mygetopt(sys.argv)    
    
    cli_sock = establish_connection(optlist)

    myserver = optlist[1]
    myserverport = int(optlist[2])
    #myipx = optlist[3]
    myport = optlist[4]
    myinterface = optlist[5]
    myname = optlist[6]
    
    if(myinterface == 'lo'):
        myIP = '127.0.0.1'
    else:
        myIP = optlist[3]
        
    ADDR=(myserver,myserverport)
    #inputx = [cli_sock,sys.stdin]
    inputx=[sys.stdin]
    
    p = pycap.capture.capture(device=myinterface)
    sys.stdout.write('%')   
    sys.stdout.flush()
 
    _thread_invoked = 0
    _run_main = 1
    th=None
    
    th = recv_class(cli_sock,myserver,myserverport,myIP,myport,p)
    th.start()
    _thread_invoked=1
    timeout=0

    while (_run_main):    
        inputready,outputready,exceptready = select.select(inputx,[],[],timeout)

        for tmp in inputready: 
            if tmp == sys.stdin:
                sys.stdout.write('%')   
                sys.stdout.flush()

                line = sys.stdin.readline()
                if line == '\n':
                    break
                line = line.strip()
                if(line[0] == '\\') and (line[1] == '@'):
                    """PRIVATE CHAT"""
                    line = line.replace('\n','')
                    
                    if(':' in line):
                        send_packet(cli_sock,line,ADDR,myport)
                    else:
                        print "[CONSOLE] for private chat use as shown below"
                        print "EXAMPLE: \@clientname:Hello, This is a private chat"
                elif(line[0] == '\\'):
                    """SPECIAL COMMAND"""
                    check_special_command(line,cli_sock,ADDR,myport,th)
                else:
                    """Group Chat"""
                    send_packet(cli_sock,line,ADDR,myport)
                    
    if(th != None):
        print "Waiting for other threads to exit"
        th.join()
        print "All threads now exit"
    cli_sock.close()
    
if __name__ == "__main__":
    main()
    
